package edu.xidian.onlinedocument.utils.ot.internal;

import edu.xidian.onlinedocument.handler.excpetions.custom.CustomSimpleException;
import edu.xidian.onlinedocument.utils.ot.internal.operations.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.LinkedList;
import java.util.List;

/**
 * Text类
 * 保存在服务器端的唯一的文档内容buffer和唯一的文档操作序列changesList
 * 以及每加入一次changesList就会增加1的revision，表示文档的版本
 */
public class Text {

    private final Logger logger = LoggerFactory.getLogger(Text.class);

    /**
     * 文档的文本内容
     */
    private final GapBuffer buffer;
    /**
     * 一系列操作序列
     */
    private final List<ChangesList> changesList;

    public Text() {
        this(new GapBuffer(""));
    }

    public Text(GapBuffer buffer) {
        this.buffer = buffer;
        this.changesList = new LinkedList<>();
        // 空白文档，即版本为0时也给list里添加一个空的ChangesList，表示版本0的操作
        changesList.add(new ChangesList());
    }

    /**
     * 保存一个操作序列至本地
     * @param changes 要保存的操作序列
     * @return true 成功 / false 失败
     * @throws CustomSimpleException 自定义异常
     */
    private synchronized boolean save(ChangesList changes) throws CustomSimpleException {
        // logger.info("save: changesList=[{}]", changes);
        if(changes.getRevision() - this.getRevision() != 1) {
            throw new CustomSimpleException("changes' revision=" + changes.getRevision() + " ,but text's revision=" + getRevision());
            // logger.warn("changes' revision=[{}], but text's revision=[{}]", changes.getRevision(), revision());
            // return false;
        }
        // 将操作添加到文档的操作列表中
        changesList.add(changes);
        // 更新版本，包括Text维护的revision和changesList里维护的revision
        // 但是List的序号其实已经可以表示changesList的版本号，因此changesList里的revision暂且保留修改但不使用
        // revision++;
        // changesList.get(this.revision - 1).setRevision(this.revision);
        return true;
    }

    /**
     * 保存一系列操作序列到本地并应用到文本上
     * @param changes 一系列操作序列
     * @return 该对象
     * @throws CustomSimpleException 自定义异常
     */
    public synchronized Text apply(List<ChangesList> changes) throws CustomSimpleException {
        for (ChangesList c:
             changes) {
            // 将操作保存在操作列表中
            this.save(c);
            /*
            // 将操作应用到文档内容中
            buffer.open();
            List<Change> changesValue = c.getChanges();
            for (Change change:
                    changesValue) {
                this.apply(change);
            }
            buffer.close();
             */
        }
        return this;
    }

    public synchronized Text apply(ChangesList changes) throws CustomSimpleException {
        // 将操作保存在操作列表中
        this.save(changes);
        /*
        // 将操作应用到文档内容中
        buffer.open();
        List<Change> changesValue = changes.getChanges();
        for (Change change:
             changesValue) {
            this.apply(change);
        }
        buffer.close();
        */
        return this;
    }

    private synchronized Text apply(Change change) {
        if (ChangeType.isRetain(change)) {
            buffer.retain(change.offset());
        }
        else if (ChangeType.isInsert(change)) {
            buffer.insert(((Insert) change).value());
        }
        else if (ChangeType.isDelete(change)) {
            buffer.delete(change.offset());
        }
        // System.out.println(buffer.build());
        return this;
    }

    /**
     * 获取某版本对应的操作序列
     * @param revision 版本号
     * @return 操作序列
     */
    public ChangesList getChangesList(int revision) {
        if(revision > 0 && revision <= this.getRevision()){
            return changesList.get(getIndex(revision));
        }
        return null;
    }

    /**
     * 获取所有的操作序列
     * @return 所有的操作序列
     */
    public List<ChangesList> getAllChangesList() {
        return changesList;
    }

    /**
     * 获取两个序号之间的操作序列
     * @param lRevision 起始序号
     * @param hRevision 结束序号
     * @return 一系列操作序列
     */
    public List<ChangesList> getBetweenChangesList(int lRevision, int hRevision) {
        return changesList.subList(getIndex(lRevision), getIndex(hRevision + 1));
    }

    public int getRevision() {
        return changesList.size() - 1;
    }

    private int getIndex(int revision) {
        return revision;
    }

    /**
     * 替换两个序号之间的操作序列
     * @param fromRevision 起始序号
     * @param toRevision 结束序号
     * @param newList 新的操作序列
     * @return 是否成功
     * @throws Exception 异常
     */
    public boolean alterSeriesChangesList(int fromRevision, int toRevision, List<ChangesList> newList) throws Exception {
        if(getIndex(fromRevision) < 0 || getIndex(toRevision) > getRevision()) {
            return false;
        }
        // 移除旧版本操作序列
        for (int i = getIndex(fromRevision); i <= getIndex(toRevision); i++) {
            changesList.remove(i);
        }
        // 增加新操作序列
        for (int j = getIndex(fromRevision); j < newList.size() - 1; j++) {
            if(j <= -1) {
                changesList.add(newList.get(0));
            }
            else {
                changesList.add(j, newList.get(j + 1));
            }
        }
        // 修改被影响的后续操作的版本
        for (int k = newList.size() - 1; k <= getIndex(this.getRevision()); k++) {
            changesList.get(k).setRevision(k);
        }
        // 重新构造buffer
        buffer.clean();
        this.apply(this.changesList);
        return true;
    }

    /**
     * 返回文档的全部文本内容
     * @return 文档的全部文本内容
     */
    public String build() {
        return buffer.build();
    }

    @Override
    public String toString() {
        String result = "";

        result += "\n-----text-----\n";
        result += buffer.build();
        result += "\n";
        /*
        result += "\n-----ops-----\n";
        for (ChangesList c:
             changesList) {
            result += "--revision=" + c.getRevision() + "\n" + c;
        }
         */

        return result;
    }
}
